package com.bckj.fastboot.core.extra.elasticsearch;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;

/**
 * ElasticsearchOps
 *
 * @author wfc
 **/
@Getter
@RequiredArgsConstructor
public class ElasticsearchOps {

    private final ElasticsearchOperations elasticsearchOperations;

    // region 索引管理操作
    /**
     * 判断索引是否存在
     * @param indexName 索引名称
     * @return 是否存在
     */
    public boolean indexExists(String indexName) {
        if (!StringUtils.hasText(indexName)) {
            return false;
        }
        IndexOperations indexOps = elasticsearchOperations.indexOps(IndexCoordinates.of(indexName));
        return indexOps.exists();
    }

    /**
     * 创建索引（基于实体类注解配置）
     * @param clazz 实体类类型
     * @return 是否创建成功
     */
    public boolean createIndex(Class<?> clazz) {
        IndexOperations indexOps = elasticsearchOperations.indexOps(clazz);
        return indexOps.create() || indexOps.putMapping();
    }

    /**
     * 删除索引
     * @param indexName 索引名称
     * @return 是否删除成功
     */
    public boolean deleteIndex(String indexName) {
        if (!StringUtils.hasText(indexName)) {
            return false;
        }
        IndexOperations indexOps = elasticsearchOperations.indexOps(IndexCoordinates.of(indexName));
        return indexOps.delete();
    }
    // endregion

    // region 文档CRUD操作
    /**
     * 保存/更新文档（ID存在则更新，不存在则新增）
     * @param entity 文档实体
     * @param <T> 实体类型
     * @return 保存后的实体
     */
    public <T> T save(T entity) {
        return elasticsearchOperations.save(entity);
    }

    /**
     * 批量保存文档
     * @param entities 文档列表
     * @param <T> 实体类型
     * @return 保存后的实体列表
     */
    public <T> Iterable<T> saveAll(List<T> entities) {
        return elasticsearchOperations.save(entities);
    }

    /**
     * 根据ID查询文档
     * @param id 文档ID
     * @param clazz 实体类类型
     * @param <T> 实体类型
     * @return 文档实体
     */
    public <T> T findById(String id, Class<T> clazz) {
        return elasticsearchOperations.get(id, clazz);
    }

    /**
     * 根据ID删除文档
     * @param id 文档ID
     * @param clazz 实体类类型
     * @return 删除的文档ID
     */
    public String deleteById(String id, Class<?> clazz) {
        return elasticsearchOperations.delete(id, clazz);
    }

    /**
     * 根据条件删除文档
     * @param fieldName 字段名
     * @param value 字段值
     * @param clazz 实体类类型
     */
    public void deleteByField(String fieldName, Object value, Class<?> clazz) {
        Criteria criteria = new Criteria(fieldName).is(value);
        Query query = new CriteriaQuery(criteria);
        elasticsearchOperations.delete(query, clazz);
    }
    // endregion

    // region 查询操作
    /**
     * 条件查询（支持分页）
     * @param criteria 查询条件
     * @param clazz 实体类类型
     * @param page 页码（从0开始）
     * @param size 每页大小
     * @param <T> 实体类型
     * @return 查询结果
     */
    public <T> SearchHits<T> searchByCriteria(Criteria criteria, Class<T> clazz, int page, int size) {
        CriteriaQuery query = new CriteriaQuery(criteria);
        query.setPageable(PageRequest.of(page, size));
        return elasticsearchOperations.search(query, clazz);
    }

    /**
     * 多字段匹配查询
     * @param keyword 关键词
     * @param fields 要匹配的字段数组
     * @param clazz 实体类类型
     * @param page 页码
     * @param size 每页大小
     * @param <T> 实体类型
     * @return 查询结果
     */
    public <T> SearchHits<T> multiMatchSearch(String keyword, String[] fields, Class<T> clazz, int page, int size) {
        Criteria criteria = new Criteria();
        for (String field : fields) {
            criteria = criteria.or(new Criteria(field).contains(keyword));
        }

        CriteriaQuery query = new CriteriaQuery(criteria);
        query.setPageable(PageRequest.of(page, size));
        return elasticsearchOperations.search(query, clazz);
    }

    /**
     * 原生JSON查询
     * @param jsonQuery JSON查询语句
     * @param clazz 实体类类型
     * @param <T> 实体类型
     * @return 查询结果
     */
    public <T> SearchHits<T> searchByJson(String jsonQuery, Class<T> clazz) {
        Query query = new StringQuery(jsonQuery);
        return elasticsearchOperations.search(query, clazz);
    }
    // endregion

    // region 高级操作
    /**
     * 批量操作
     * @param indexQueries 索引查询列表
     * @param indexName 索引名称
     */
    public void bulkOperations(List<IndexQuery> indexQueries, String indexName) {
        elasticsearchOperations.bulkIndex(indexQueries, IndexCoordinates.of(indexName));
    }

    /**
     * 更新文档字段
     * @param id 文档ID
     * @param updateFields 要更新的字段Map
     * @param clazz 实体类类型
     * @param indexName 索引名称
     */
    public void updateDocument(String id, Map<String, Object> updateFields, Class<?> clazz, String indexName) {
        UpdateQuery updateQuery = UpdateQuery.builder(id)
                .withDocument(Document.from(updateFields))
                .build();
        elasticsearchOperations.update(updateQuery, IndexCoordinates.of(indexName));
    }
    // endregion
}
